package parser
import (
"fmt"
"github.com/ChrisTrenkamp/goxpath/lexer"
)
type stateType int
const (
defState stateType = iota
xpathState
funcState
paramState
predState
parenState
)
type parseStack struct {
stack []*Node
stateTypes []stateType
cur *Node
}
func (p *parseStack ) push (t stateType ) {
p .stack = append (p .stack , p .cur )
p .stateTypes = append (p .stateTypes , t )
}
func (p *parseStack ) pop () {
stackPos := len (p .stack ) - 1
p .cur = p .stack [stackPos ]
p .stack = p .stack [:stackPos ]
p .stateTypes = p .stateTypes [:stackPos ]
}
func (p *parseStack ) curState () stateType {
if len (p .stateTypes ) == 0 {
return defState
}
return p .stateTypes [len (p .stateTypes )-1 ]
}
type lexFn func (*parseStack , lexer .XItem )
var parseMap = map [lexer .XItemType ]lexFn {
lexer .XItemAbsLocPath : xiXPath ,
lexer .XItemAbbrAbsLocPath : xiXPath ,
lexer .XItemAbbrRelLocPath : xiXPath ,
lexer .XItemRelLocPath : xiXPath ,
lexer .XItemEndPath : xiEndPath ,
lexer .XItemAxis : xiXPath ,
lexer .XItemAbbrAxis : xiXPath ,
lexer .XItemNCName : xiXPath ,
lexer .XItemQName : xiXPath ,
lexer .XItemNodeType : xiXPath ,
lexer .XItemProcLit : xiXPath ,
lexer .XItemFunction : xiFunc ,
lexer .XItemArgument : xiFuncArg ,
lexer .XItemEndFunction : xiEndFunc ,
lexer .XItemPredicate : xiPred ,
lexer .XItemEndPredicate : xiEndPred ,
lexer .XItemStrLit : xiValue ,
lexer .XItemNumLit : xiValue ,
lexer .XItemOperator : xiOp ,
lexer .XItemVariable : xiValue ,
}
var opPrecedence = map [string ]int {
"|" : 1 ,
"*" : 2 ,
"div" : 2 ,
"mod" : 2 ,
"+" : 3 ,
"-" : 3 ,
"=" : 4 ,
"!=" : 4 ,
"<" : 4 ,
"<=" : 4 ,
">" : 4 ,
">=" : 4 ,
"and" : 5 ,
"or" : 6 ,
}
func Parse (xp string ) (*Node , error ) {
var err error
c := lexer .Lex (xp )
n := &Node {}
p := &parseStack {cur : n }
for next := range c {
if next .Typ != lexer .XItemError {
parseMap [next .Typ ](p , next )
} else if err == nil {
err = fmt .Errorf (next .Val )
}
}
return n , err
}
func xiXPath(p *parseStack , i lexer .XItem ) {
if p .curState () == xpathState {
p .cur .push (i )
p .cur = p .cur .next
return
}
if p .cur .Val .Typ == lexer .XItemFunction {
p .cur .Right = &Node {Val : i , Parent : p .cur }
p .cur .next = p .cur .Right
p .push (xpathState )
p .cur = p .cur .next
return
}
p .cur .pushNotEmpty (i )
p .push (xpathState )
p .cur = p .cur .next
}
func xiEndPath(p *parseStack , i lexer .XItem ) {
p .pop ()
}
func xiFunc(p *parseStack , i lexer .XItem ) {
if p .cur .Val .Typ == Empty {
p .cur .pushNotEmpty (i )
p .push (funcState )
p .cur = p .cur .next
return
}
p .cur .push (i )
p .cur = p .cur .next
p .push (funcState )
}
func xiFuncArg(p *parseStack , i lexer .XItem ) {
if p .curState () != funcState {
p .pop ()
}
p .cur .push (i )
p .cur = p .cur .next
p .push (paramState )
p .cur .push (lexer .XItem {Typ : Empty , Val : "" })
p .cur = p .cur .next
}
func xiEndFunc(p *parseStack , i lexer .XItem ) {
if p .curState () == paramState {
p .pop ()
}
p .pop ()
}
func xiPred(p *parseStack , i lexer .XItem ) {
p .cur .push (i )
p .cur = p .cur .next
p .push (predState )
p .cur .push (lexer .XItem {Typ : Empty , Val : "" })
p .cur = p .cur .next
}
func xiEndPred(p *parseStack , i lexer .XItem ) {
p .pop ()
}
func xiValue(p *parseStack , i lexer .XItem ) {
p .cur .add (i )
}
func xiOp(p *parseStack , i lexer .XItem ) {
if i .Val == "(" {
p .cur .push (lexer .XItem {Typ : Empty , Val : "" })
p .push (parenState )
p .cur = p .cur .next
return
}
if i .Val == ")" {
p .pop ()
return
}
if p .cur .Val .Typ == lexer .XItemOperator {
if opPrecedence [p .cur .Val .Val ] <= opPrecedence [i .Val ] {
p .cur .add (i )
} else {
p .cur .push (i )
}
} else {
p .cur .add (i )
}
p .cur = p .cur .next
}
The pages are generated with Golds v0.6.7 . (GOOS=linux GOARCH=amd64)
Golds is a Go 101 project developed by Tapir Liu .
PR and bug reports are welcome and can be submitted to the issue list .
Please follow @Go100and1 (reachable from the left QR code) to get the latest news of Golds .